﻿using System;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.IdentityModel.Tokens;
using System.Collections.Generic;

namespace SecurityTokenServiceNS
{
	#region StsConfiguration
	public class StsConfiguration : ConfigurationSection
    {
        const string SectionName = "stsConfiguration";
        static StsConfiguration _configuration = null;

        public static StsConfiguration Current
        {
            get
            {
                if (_configuration == null)
                {
                    _configuration = (StsConfiguration)ConfigurationManager.GetSection(StsConfiguration.SectionName);
                    if (_configuration == null)
                        throw new ConfigurationErrorsException("Configuration STS Section not found!");
                }
                return _configuration;
            }
        }

        [ConfigurationProperty("stsTokens", IsRequired=true)]
        public ServiceCertificateCollection StsTokens
        {
            get { return (ServiceCertificateCollection)this["stsTokens"]; }
        }
		[ConfigurationProperty("rpTokens", IsRequired = true)]
        public ServiceCertificateCollection RpTokens
        {
            get { return (ServiceCertificateCollection)this["rpTokens"]; }
        }
		[ConfigurationProperty("extensionModules", IsRequired = true)]
		public ExtensionModulesConfiguration ExtensionModules
		{
			get { return (ExtensionModulesConfiguration)this["extensionModules"]; }
		}
		[ConfigurationProperty("claimMappings", IsRequired = true)]
		public ClaimMappingsCollection ClaimMappings
		{
			get { return (ClaimMappingsCollection)this["claimMappings"]; }
		}
		[ConfigurationProperty("claimPolicies", IsRequired = true)]
		public ExtensionModuleCollection ClaimPolicies
		{
			get { return (ExtensionModuleCollection)this["claimPolicies"]; }
		}
		[ConfigurationProperty("azMan")]
		public GeneralConfigurationCollection AzManCollection
		{
			get { return (GeneralConfigurationCollection)this["azMan"]; }
		}

		#region public and auxiliar methods
		public SecurityToken GetStsToken()
        {
            EndpointAddress endpoint = new EndpointAddress(Constants.WSTrust.Actions0502.Issue);

            if (!StsTokens.Contains(endpoint))
                throw new ConfigurationErrorsException("Configuration STS Token unknown!");

            return GetCertificate(StsTokens.Item(endpoint));
        }

        public SecurityToken GetRpToken(EndpointAddress appliesTo)
        {
            if (appliesTo == null)
                throw new ArgumentNullException("appliesTo");

			if (RpTokens.Db)
				return DbConfiguration.Current.GetRpToken(appliesTo);
			else if (!RpTokens.Contains(appliesTo))
				return null;

            return GetCertificate(RpTokens.Item(appliesTo));
        }

        private X509SecurityToken GetCertificate(ServiceCertificateConfiguration certificate)
        {
            X509Store store = new X509Store(certificate.StoreName, certificate.StoreLocation);
            X509SecurityToken token = null;
            try
            {
                store.Open(OpenFlags.OpenExistingOnly);
                X509Certificate2Collection collection = store.Certificates.Find(certificate.FindType, certificate.FindValue, true);
                if (collection.Count == 0)
                    throw new Exception(certificate.FindValue + " Certificate not found");
                token = new X509SecurityToken(collection[0]);
            }
            finally
            {
                store.Close();
            }
            return token;
		}
		#endregion

	}
	#endregion

    #region ServiceCertificateCollection
    [ConfigurationCollection( typeof(ServiceCertificateConfiguration) )]
    public class ServiceCertificateCollection : ConfigurationElementCollection
    {
		[ConfigurationProperty("db", IsRequired = false, DefaultValue = false)]
		public bool Db
		{
			get { return (bool)this["db"]; }
		}

        protected override ConfigurationElement CreateNewElement()
        {
            return new ServiceCertificateConfiguration();
        }

        protected override Object GetElementKey(ConfigurationElement element)
        {
            return new EndpointAddress(((ServiceCertificateConfiguration)element).Uri.ToString());
        }

        internal bool Contains(EndpointAddress endpoint)
        {
            return Item(endpoint) != null;
        }

        internal ServiceCertificateConfiguration Item(EndpointAddress endpoint)
        {
            foreach (ServiceCertificateConfiguration scc in this)
            {
                if (scc.Uri == endpoint.Uri)
                    return scc;
            }
            return null;
        }
	}
	#endregion

    #region ServiceCertificateConfiguration

    public class ServiceCertificateConfiguration : ConfigurationElement
    {
        public ServiceCertificateConfiguration() { }

        [ConfigurationProperty("uri", IsRequired = true, IsKey = true)]
        public Uri Uri
        {
            get { return (Uri)this["uri"]; }
        }

        [ConfigurationProperty("storeLocation", IsRequired = true, DefaultValue = StoreLocation.LocalMachine)]
        public StoreLocation StoreLocation
        {
            get { return (StoreLocation)this["storeLocation"]; }
        }

        [ConfigurationProperty("storeName", IsRequired = true, DefaultValue = StoreName.My)]
        public StoreName StoreName
        {
            get { return (StoreName)this["storeName"]; }
        }

        [ConfigurationProperty("findValue", IsRequired = true)]
        public string FindValue
        {
            get { return (string)this["findValue"]; }
        }

        [ConfigurationProperty("findType", IsRequired = true, DefaultValue = X509FindType.FindBySubjectDistinguishedName)]
        public X509FindType FindType
        {
            get { return (X509FindType)this["findType"]; }
        }
    }
	#endregion

	#region ExtensionModulesConfiguration
	public class ExtensionModulesConfiguration : ConfigurationElement
	{
		public ExtensionModulesConfiguration() { }

		[ConfigurationProperty("path", IsRequired = true)]
		public string Path
		{
			get { return (string)this["path"]; }
		}
		[ConfigurationProperty("rstProcessor")]
		public ExtensionModuleCollection RstProcessor
		{
			get { return (ExtensionModuleCollection)this["rstProcessor"]; }
		}
		[ConfigurationProperty("proofProcessor")]
		public ExtensionModuleCollection ProofProcessor
		{
			get { return (ExtensionModuleCollection)this["proofProcessor"]; }
		}
		[ConfigurationProperty("issuedClaimsProcessor")]
		public ExtensionModuleCollection IssuedClaimsProcessor
		{
			get { return (ExtensionModuleCollection)this["issuedClaimsProcessor"]; }
		}
		[ConfigurationProperty("issuedTokenProcessor")]
		public ExtensionModuleCollection IssuedTokenProcessor
		{
			get { return (ExtensionModuleCollection)this["issuedTokenProcessor"]; }
		}
		[ConfigurationProperty("rstrProcessor")]
		public ExtensionModuleCollection RstrProcessor
		{
			get { return (ExtensionModuleCollection)this["rstrProcessor"]; }
		}
	}
	#endregion

	#region ExtensionModuleCollection
	[ConfigurationCollection(typeof(ExtensionModuleConfiguration))]
	public class ExtensionModuleCollection : ConfigurationElementCollection
	{
		protected override ConfigurationElement CreateNewElement()
		{
			return new ExtensionModuleConfiguration();
		}
		protected override Object GetElementKey(ConfigurationElement element)
		{
			return ((ExtensionModuleConfiguration)element).Type;
		}
		internal ExtensionModuleConfiguration GetFirst()
		{
			foreach (ExtensionModuleConfiguration aic in this)
				return aic;
			return null;
		}
	}
	#endregion

	#region ExtensionModuleConfiguration
	public class ExtensionModuleConfiguration : ConfigurationElement
	{
		public ExtensionModuleConfiguration() { }

		[ConfigurationProperty("type", IsRequired = true, IsKey = true)]
		public string Type
		{
			get { return (string)this["type"]; }
		}
		[ConfigurationProperty("lib", IsRequired = true)]
		public string Lib
		{
			get { return (string)this["lib"]; }
		}
	}
	#endregion

    #region ClaimMappingsCollection
    [ConfigurationCollection( typeof(ClaimMappingsConfiguration) )]
    public class ClaimMappingsCollection : ConfigurationElementCollection
    {
		[ConfigurationProperty("db", IsRequired = false, DefaultValue = false)]
		public bool Db
		{
			get { return (bool)this["db"]; }
		}
		protected override ConfigurationElement CreateNewElement()
		{
		    return new ClaimMappingsConfiguration();
		}
		protected override Object GetElementKey(ConfigurationElement element)
		{
			return ((ClaimMappingsConfiguration)element).Key;
		}
		public List<ClaimMapping> GetMappingByEndpoint(EndpointAddress endpoint)
		{
			if (this.Db)
				return DbConfiguration.Current.GetMappingByEndpoint(endpoint);
			else
			{
				List<ClaimMapping> list = new List<ClaimMapping>();
				foreach (ClaimMappingsConfiguration cmc in this)
					if (cmc.Active && cmc.Endpoint == endpoint.Uri)
						list.Add(new ClaimMapping(cmc.FromType,cmc.ToType,cmc.FromValue,cmc.ToValue));
				return list;
			}
		}
	}
	#endregion

	#region ClaimMappingsConfiguration
	public class ClaimMappingsConfiguration : ConfigurationElement
	{
		public ClaimMappingsConfiguration() { }

		[ConfigurationProperty("key", IsRequired = true, IsKey = true)]
		public string Key
		{
			get { return (string)this["key"]; }
		}
		[ConfigurationProperty("endpoint", IsRequired = true)]
		public Uri Endpoint
		{
			get { return (Uri)this["endpoint"]; }
		}
		[ConfigurationProperty("active", DefaultValue=true)]
		public bool Active
		{
			get { return (bool)this["active"]; }
		}
		[ConfigurationProperty("fromType", IsRequired = true)]
		public string FromType
		{
			get { return (string)this["fromType"]; }
		}
		[ConfigurationProperty("toType")]
		public string ToType
		{
			get { return (string)this["toType"]; }
		}
		[ConfigurationProperty("fromValue")]
		public string FromValue
		{
			get { return (string)this["fromValue"]; }
		}
		[ConfigurationProperty("toValue")]
		public string ToValue
		{
			get { return (string)this["toValue"]; }
		}
	}
	#endregion

	#region ClaimMapping
	public struct ClaimMapping
	{
		public string FromType, ToType, FromValue, ToValue;
		public ClaimMapping(string fromType, string toType, string fromValue, string toValue)
		{
			FromType = fromType;
			ToType = toType;
			FromValue = fromValue;
			ToValue = toValue;
		}
	}
	#endregion

	#region GeneralConfigurationCollection
	[ConfigurationCollection(typeof(GeneralConfiguration))]
	public class GeneralConfigurationCollection : ConfigurationElementCollection
	{
		protected override ConfigurationElement CreateNewElement()
		{
			return new GeneralConfiguration();
		}
		protected override Object GetElementKey(ConfigurationElement element)
		{
			return ((GeneralConfiguration)element).Key;
		}
		public string GetValueByKey(string key)
		{
			foreach (GeneralConfiguration gcc in this)
				if (gcc.Key == key)
					return gcc.Value;
			return null;
		}
	}
	#endregion

	#region GeneralConfiguration
	public class GeneralConfiguration : ConfigurationElement
	{
		public GeneralConfiguration() { }

		[ConfigurationProperty("key", IsKey = true, IsRequired = true)]
		public string Key
		{
			get { return (string)this["key"]; }
		}
		[ConfigurationProperty("value", IsRequired = true)]
		public string Value
		{
			get { return (string)this["value"]; }
		}
	}
	#endregion
}
